package com.aabte.auth.util;

import com.aabte.auth.account.domain.model.Account;
import com.aabte.auth.group.domain.model.Group;
import com.aabte.auth.role.domain.model.Role;
import com.aabte.auth.role.domain.model.RoleName;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.Getter;

import javax.crypto.SecretKey;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

public class JwtUtil {

    private static final SecretKey SECRET_KEY = secretKey();

    private static final long TTL_MILLIS = 24 * 60 * 60;

    @Getter
    public static class AccountDTO {
        @JsonProperty(value = "account_id")
        private String accountId;
        @JsonProperty(value = "account_name")
        private String accountName;
        private Set<String> roles;
    }

    public static String createJwt(Account account) {
        long nowMillis = Instant.now().toEpochMilli();
        Date now = new Date(nowMillis);
        JwtBuilder jwtBuilder = Jwts.builder()
                .setId(UUID.randomUUID().toString().replace("-", ""))
                .setSubject(account.getAccountId().getId())
                .setIssuer("account-service")
                .setIssuedAt(now)
                .addClaims(Collections.singletonMap("account", getAccountJson(account)))
                .signWith(SECRET_KEY, signatureAlgorithm());
        if (TTL_MILLIS >= 0) {
            long expMillis = nowMillis + TTL_MILLIS;
            Date expDate = new Date(expMillis);
            jwtBuilder.setExpiration(expDate);
        }
        return jwtBuilder.base64UrlEncodeWith(bytes -> Base64.getEncoder().encodeToString(bytes))
                .compact();
    }

    private static AccountDTO getAccountJson(Account account) {
        AccountDTO accountDto = new AccountDTO();
        accountDto.accountId = account.getAccountId().getId();
        accountDto.accountName = account.getAccountName().getName();
        accountDto.roles = getRoles(account.getGroups());
        return accountDto;
    }

    private static Set<String> getRoles(Set<Group> groups) {
        return groups.stream()
                .map(Group::getRoles).flatMap(Set::stream)
                .map(Role::getRoleName)
                .map(RoleName::getName)
                .collect(Collectors.toSet());
    }

    public static Claims parseJwt(String jwt) {
        return Jwts.parserBuilder()
                .setSigningKey(SECRET_KEY)
                .base64UrlDecodeWith(s -> Base64.getDecoder().decode(s))
                .build()
                .parseClaimsJws(jwt)
                .getBody();
    }

    private static SecretKey secretKey() {
        return Keys.secretKeyFor(signatureAlgorithm());
    }

    private static SignatureAlgorithm signatureAlgorithm() {
        return SignatureAlgorithm.HS512;
    }

}
